package com.ittao.taoaicodeapp.controller;

import com.ittao.taoaicodecommon.manage.storage.enums.StorageType;
import com.ittao.taoaicodecommon.manage.storage.factory.FileStorageStrategyFactory;
import com.ittao.taoaicodecommon.manage.storage.strategy.FileStorageStrategy;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 文件代理控制器
 * 处理Nginx转发的文件请求，根据当前存储策略返回文件内容
 */
@Slf4j
@RestController
@RequestMapping("/file/proxy")
public class FileProxyController {

    @jakarta.annotation.Resource
    private FileStorageStrategyFactory storageStrategyFactory;
    
    // 添加下载状态缓存，避免重复下载
    private final ConcurrentHashMap<String, CompletableFuture<Boolean>> downloadingFiles = new ConcurrentHashMap<>();

    /**
     * 代理code_output文件请求
     * 路径格式: /file/proxy/{userId}/code_output/{fileName}
     */
    @GetMapping("/{userId}/code_output/**")
    public ResponseEntity<Resource> proxyCodeOutput(
            @PathVariable String userId,
            HttpServletRequest request) {

        try {
            // 提取文件名部分
            String requestURI = request.getRequestURI();
            String prefix = "/file/proxy/" + userId + "/code_output/";
            String fileName = requestURI.substring(requestURI.indexOf(prefix) + prefix.length());

            // 如果没有指定文件名或者是目录访问（以/结尾），默认返回index.html
            if (fileName.isEmpty() || fileName.endsWith("/")) {
                fileName = fileName.isEmpty() ? "index.html" : fileName + "index.html";
            }

            log.info("代理code_output文件请求: userId={}, fileName={}", userId, fileName);

            FileStorageStrategy strategy = storageStrategyFactory.getCurrentStrategy();
            
            // 根据存储策略采用不同的处理方式
            if (StorageType.COS.equals(strategy.getStorageType())) {
                return handleCosStrategy(strategy, userId, fileName);
            } else {
                return handleLocalStrategy(strategy, userId, fileName);
            }

        } catch (IOException e) {
            log.error("代理code_output文件失败: userId={}, error={}", userId, e.getMessage());
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            log.error("代理code_output文件异常: userId={}", userId, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * 代理code_deploy文件请求
     * 路径格式: /file/proxy/{userId}/code_deploy/{deployKey}/{fileName}
     */
    @GetMapping("/{userId}/code_deploy/{deployKey}/**")
    public ResponseEntity<Resource> proxyCodeDeploy(
            @PathVariable String userId,
            @PathVariable String deployKey,
            HttpServletRequest request) {

        try {
            // 提取文件名部分
            String requestURI = request.getRequestURI();
            String prefix = "/file/proxy/" + userId + "/code_deploy/" + deployKey + "/";
            String fileName = requestURI.substring(requestURI.indexOf(prefix) + prefix.length());

            // 如果没有指定文件名，默认返回index.html
            if (fileName.isEmpty()) {
                fileName = "index.html";
            }

            log.debug("代理code_deploy文件请求: userId={}, deployKey={}, fileName={}", userId, deployKey, fileName);

            FileStorageStrategy strategy = storageStrategyFactory.getCurrentStrategy();
            
            // 根据存储策略采用不同的处理方式
            if (StorageType.COS.equals(strategy.getStorageType())) {
                return handleCosDeployStrategy(strategy, userId, deployKey, fileName);
            } else {
                return handleLocalDeployStrategy(strategy, userId, deployKey, fileName);
            }

        } catch (IOException e) {
            log.error("代理code_deploy文件失败: userId={}, deployKey={}, error={}", userId, deployKey, e.getMessage());
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            log.error("代理code_deploy文件异常: userId={}, deployKey={}", userId, deployKey, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * 处理COS策略模式下的code_output文件请求
     * COS策略：优先返回本地缓存，不存在时从COS下载并缓存
     */
    private ResponseEntity<Resource> handleCosStrategy(FileStorageStrategy strategy, String userId, String fileName) throws IOException {
        log.info("COS策略处理code_output文件请求: userId={}, fileName={}", userId, fileName);
        
        String cacheKey = userId + "_code_output_" + fileName;
        
        try {
            // 1. 构建本地临时文件路径（用于缓存）
            String localFilePath = strategy.buildCodeOutputPath(userId, fileName);
            Path localPath = Paths.get(localFilePath);
            
            // 2. 检查本地临时文件是否存在且有效
            if (Files.exists(localPath) && Files.isRegularFile(localPath) && Files.size(localPath) > 0) {
                log.info("返回本地缓存文件: {}", localFilePath);
                return buildFileResponse(new FileSystemResource(localPath), fileName);
            }
            
            // 3. 检查是否正在下载
            CompletableFuture<Boolean> downloadFuture = downloadingFiles.get(cacheKey);
            if (downloadFuture != null) {
                try {
                    log.info("等待文件下载完成: {}", cacheKey);
                    // 等待下载完成，最多等待5秒
                    downloadFuture.get(5, TimeUnit.SECONDS);
                    
                    // 下载完成后再次检查文件
                    if (Files.exists(localPath) && Files.isRegularFile(localPath) && Files.size(localPath) > 0) {
                        log.info("下载完成，返回本地缓存文件: {}", localFilePath);
                        return buildFileResponse(new FileSystemResource(localPath), fileName);
                    }
                } catch (Exception e) {
                    log.warn("等待文件下载超时或失败: {}, error={}", cacheKey, e.getMessage());
                    // 继续执行下载逻辑
                }
            }
            
            // 4. 本地文件不存在或无效，从COS读取并缓存
            log.info("本地缓存文件不存在，从COS读取: {}", fileName);
            
            // 添加同步锁，避免并发下载同一文件
            String lockKey = cacheKey.intern();
            synchronized (lockKey) {
                // 再次检查文件是否已被其他线程下载
                if (Files.exists(localPath) && Files.isRegularFile(localPath) && Files.size(localPath) > 0) {
                    log.info("文件已被其他线程下载: {}", localFilePath);
                    return buildFileResponse(new FileSystemResource(localPath), fileName);
                }
                
                // 标记正在下载
                CompletableFuture<Boolean> future = new CompletableFuture<>();
                downloadingFiles.put(cacheKey, future);
                
                try {
                    // 从COS下载文件
                    Resource resource = strategy.readFile(localFilePath);
                    
                    // 标记下载完成
                    future.complete(true);
                    downloadingFiles.remove(cacheKey);
                    
                    log.info("COS文件下载完成: {}", localFilePath);
                    return buildFileResponse(resource, fileName);
                    
                } catch (IOException e) {
                    // 标记下载失败
                    future.completeExceptionally(e);
                    downloadingFiles.remove(cacheKey);
                    throw e;
                }
            }
            
        } catch (IOException e) {
            log.error("COS策略处理文件请求失败: userId={}, fileName={}, error={}", userId, fileName, e.getMessage());
            // 清理下载状态
            downloadingFiles.remove(cacheKey);
            // 对于前端重试机制，返回404而不是抛出异常
            return ResponseEntity.notFound().build();
        }
    }
    
    /**
     * 处理Local策略模式下的code_output文件请求
     * Local策略：直接返回本地文件路径和内容
     */
    private ResponseEntity<Resource> handleLocalStrategy(FileStorageStrategy strategy, String userId, String fileName) throws IOException {
        log.info("Local策略处理code_output文件请求: userId={}, fileName={}", userId, fileName);
        
        try {
            String filePath = strategy.buildCodeOutputPath(userId, fileName);
            
            // 检查本地文件是否存在
            Path localPath = Paths.get(filePath);
            if (!Files.exists(localPath)) {
                log.warn("Local策略：文件不存在 - {}", filePath);
                return ResponseEntity.notFound().build();
            }
            
            Resource resource = strategy.readFile(filePath);
            return buildFileResponse(resource, fileName);
            
        } catch (IOException e) {
            log.error("Local策略处理文件请求失败: userId={}, fileName={}, error={}", userId, fileName, e.getMessage());
            return ResponseEntity.notFound().build();
        }
    }
    
    /**
     * 处理COS策略模式下的code_deploy文件请求
     */
    private ResponseEntity<Resource> handleCosDeployStrategy(FileStorageStrategy strategy, String userId, String deployKey, String fileName) throws IOException {
        log.info("COS策略处理code_deploy文件请求: userId={}, deployKey={}, fileName={}", userId, deployKey, fileName);
        
        String cacheKey = userId + "_code_deploy_" + deployKey + "_" + fileName;
        
        try {
            String deployPath = strategy.buildCodeDeployPath(userId, deployKey);
            String filePath = deployPath + "/" + fileName;
            Path localPath = Paths.get(filePath);
            
            // 确保部署目录存在
            Path deployDir = Paths.get(deployPath);
            if (!Files.exists(deployDir)) {
                String dirLockKey = (userId + "_deploy_dir_" + deployKey).intern();
                synchronized (dirLockKey) {
                    if (!Files.exists(deployDir)) {
                        try {
                            Files.createDirectories(deployDir);
                            log.info("创建部署目录: {}", deployPath);
                        } catch (IOException e) {
                            log.error("创建部署目录失败: {}, error={}", deployPath, e.getMessage());
                            throw e;
                        }
                    }
                }
            }
            
            // 检查本地临时文件是否存在且有效
            if (Files.exists(localPath) && Files.isRegularFile(localPath) && Files.size(localPath) > 0) {
                log.info("返回本地临时部署文件: {}", filePath);
                return buildFileResponse(new FileSystemResource(localPath), fileName);
            }
            
            // 检查是否正在下载
            CompletableFuture<Boolean> downloadFuture = downloadingFiles.get(cacheKey);
            if (downloadFuture != null) {
                try {
                    log.info("等待部署文件下载完成: {}", cacheKey);
                    downloadFuture.get(5, TimeUnit.SECONDS);
                    
                    if (Files.exists(localPath) && Files.isRegularFile(localPath) && Files.size(localPath) > 0) {
                        log.info("部署文件下载完成，返回本地缓存: {}", filePath);
                        return buildFileResponse(new FileSystemResource(localPath), fileName);
                    }
                } catch (Exception e) {
                    log.warn("等待部署文件下载超时或失败: {}, error={}", cacheKey, e.getMessage());
                }
            }
            
            // 本地文件不存在，从COS读取
            log.info("本地临时文件不存在，从COS读取部署文件");
            
            String lockKey = cacheKey.intern();
            synchronized (lockKey) {
                // 再次检查文件是否已被其他线程下载
                if (Files.exists(localPath) && Files.isRegularFile(localPath) && Files.size(localPath) > 0) {
                    log.info("部署文件已被其他线程下载: {}", filePath);
                    return buildFileResponse(new FileSystemResource(localPath), fileName);
                }
                
                // 标记正在下载
                CompletableFuture<Boolean> future = new CompletableFuture<>();
                downloadingFiles.put(cacheKey, future);
                
                Resource resource;
                try {
                    resource = strategy.readFile(filePath);
                    future.complete(true);
                    downloadingFiles.remove(cacheKey);
                } catch (IOException e) {
                    future.completeExceptionally(e);
                    downloadingFiles.remove(cacheKey);
                    
                    // 如果文件不存在且是HTML请求，尝试返回index.html（支持Vue路由）
                    if (fileName.endsWith(".html") || !fileName.contains(".")) {
                        String indexPath = deployPath + "/index.html";
                        log.info("尝试返回index.html以支持Vue路由: {}", indexPath);
                        
                        String indexCacheKey = userId + "_code_deploy_" + deployKey + "_index.html";
                        CompletableFuture<Boolean> indexFuture = new CompletableFuture<>();
                        downloadingFiles.put(indexCacheKey, indexFuture);
                        
                        try {
                            resource = strategy.readFile(indexPath);
                            indexFuture.complete(true);
                            downloadingFiles.remove(indexCacheKey);
                        } catch (IOException indexE) {
                            indexFuture.completeExceptionally(indexE);
                            downloadingFiles.remove(indexCacheKey);
                            throw indexE;
                        }
                    } else {
                        throw e;
                    }
                }
                
                return buildFileResponse(resource, fileName);
            }
            
        } catch (IOException e) {
            log.error("COS策略处理部署文件请求失败: userId={}, deployKey={}, fileName={}, error={}", 
                     userId, deployKey, fileName, e.getMessage());
            // 清理下载状态
            downloadingFiles.remove(cacheKey);
            return ResponseEntity.notFound().build();
        }
    }
    
    /**
     * 处理Local策略模式下的code_deploy文件请求
     */
    private ResponseEntity<Resource> handleLocalDeployStrategy(FileStorageStrategy strategy, String userId, String deployKey, String fileName) throws IOException {
        log.info("Local策略处理code_deploy文件请求: userId={}, deployKey={}, fileName={}", userId, deployKey, fileName);
        
        try {
            String deployPath = strategy.buildCodeDeployPath(userId, deployKey);
            String filePath = deployPath + "/" + fileName;
            
            // 检查部署目录是否存在
            Path deployDir = Paths.get(deployPath);
            if (!Files.exists(deployDir) || !Files.isDirectory(deployDir)) {
                log.warn("Local策略：部署目录不存在 - {}", deployPath);
                return ResponseEntity.notFound().build();
            }
            
            Resource resource;
            try {
                resource = strategy.readFile(filePath);
            } catch (IOException e) {
                // 如果文件不存在且是HTML请求，尝试返回index.html（支持Vue路由）
                if (fileName.endsWith(".html") || !fileName.contains(".")) {
                    String indexPath = deployPath + "/index.html";
                    resource = strategy.readFile(indexPath);
                } else {
                    throw e;
                }
            }
            
            return buildFileResponse(resource, fileName);
            
        } catch (IOException e) {
            log.error("Local策略处理部署文件请求失败: userId={}, deployKey={}, fileName={}, error={}", 
                     userId, deployKey, fileName, e.getMessage());
            return ResponseEntity.notFound().build();
        }
    }
    
    /**
     * 构建文件响应
     */
    private ResponseEntity<Resource> buildFileResponse(Resource resource, String fileName) {
        HttpHeaders headers = new HttpHeaders();
        String contentType = getContentType(fileName);
        headers.setContentType(MediaType.parseMediaType(contentType));
        
        // 对于HTML文件，设置缓存控制
        if ("text/html".equals(contentType)) {
            headers.setCacheControl("no-cache, no-store, must-revalidate");
            headers.setPragma("no-cache");
            headers.setExpires(0);
        }
        
        return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
    }
    
    /**
     * 根据文件名获取内容类型
     */
    private String getContentType(String fileName) {
        String contentType = URLConnection.guessContentTypeFromName(fileName);
        if (contentType != null) {
            return contentType;
        }

        // 手动判断一些常见类型
        String extension = "";
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0) {
            extension = fileName.substring(lastDotIndex + 1).toLowerCase();
        }

        return switch (extension) {
            case "html" -> "text/html";
            case "css" -> "text/css";
            case "js" -> "application/javascript";
            case "json" -> "application/json";
            case "png" -> "image/png";
            case "jpg", "jpeg" -> "image/jpeg";
            case "gif" -> "image/gif";
            case "svg" -> "image/svg+xml";
            case "ico" -> "image/x-icon";
            case "woff" -> "font/woff";
            case "woff2" -> "font/woff2";
            case "ttf" -> "font/ttf";
            case "eot" -> "application/vnd.ms-fontobject";
            default -> "application/octet-stream";
        };
    }
}